﻿using System;
using ShureValidation.Language;

namespace ShureValidation.Rules
{
    /// <summary>
    /// Makes sure that only digits have been entered.
    /// </summary>
    /// <para>
    /// Language file items:
    /// <list type="table">
    ///     <listheader>
    ///         <term>ItemName</term>
    ///         <description>Language text</description>
    ///     </listheader>
    ///     <item>
    ///         <term>Digits</term>
    ///         <description>'{0}' may only contain digits.</description>
    ///     </item>
    ///     <item>
    ///         <term>DigitsExtra</term>
    ///         <description>'{0}' may only contain digits and "{1}".</description>
    ///     </item>
    /// </list>
    /// </para>
    [LanguagePrompt("Digits", "'{0}' may only contain digits.")]
    [LanguagePrompt("DigitsExtra", "'{0}' may only contain digits and '{1}'.")]
    [Serializable]
    public class DigitsRule : IRule
    {
        private readonly string _extraChars;

        /// <summary>
        /// Initializes a new instance of the <see cref="ValidateAttribute"/> class.
        /// </summary>
        public DigitsRule()
        {
            _extraChars = string.Empty;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ValidateAttribute"/> class.
        /// </summary>
        /// <param name="extraChars">extra characters that are valid.</param>
        /// <exception cref="ArgumentNullException">Argument is <c>null</c>.</exception>
        public DigitsRule(string extraChars)
        {
            _extraChars = extraChars ?? string.Empty;
        }

        #region IRule Members

        /// <summary>
        /// Validate a field value
        /// </summary>
        /// <param name="value">Value to validate</param>
        /// <returns><c>true</c> if validation was successful; otherwise <c>false</c>.</returns>
        /// <exception cref="NotSupportedException">Supports only string values.</exception>
        public bool Validate(object value)
        {
            if (ValueChecker.IsEmpty(value))
                return true;

            if (value is string)
            {
                var s = (string) value;
                foreach (char ch in s)
                {
                    if (char.IsDigit(ch) || _extraChars.IndexOf(ch) != -1)
                        continue;

                    return false;
                }
                return true;
            }

            throw new NotSupportedException(GetType() + " only supports strings.");
        }

        /// <summary>
        /// Format an error message
        /// </summary>
        /// <param name="fieldName">Field (have already been translated)</param>
        /// <param name="rulesLanguage">Language prompts for validation rules</param>
        /// <returns>Error message formatted for the current language.</returns>
        public string Format(string fieldName, IModelLanguage rulesLanguage)
        {
            return string.IsNullOrEmpty(_extraChars) == false
                       ? string.Format(rulesLanguage["DigitsExtra"], fieldName, "1234567890" + _extraChars)
                       : string.Format(rulesLanguage["Digits"], fieldName);
        }

        /// <summary>
        /// Checks if this rule support values of the specified format.
        /// </summary>
        /// <param name="type">Type of value</param>
        /// <returns><c>true</c> if the specified type can be validated; otherwise <c>false</c>.</returns>
        public bool SupportsType(Type type)
        {
            return typeof (string).IsAssignableFrom(type);
        }

        #endregion
    }
}